#include "Camera.h"
#include "NeroCore\NMemoryManager.h"
#include "Utility.h"
#include "Buffer.h"

using namespace Graphics;

RTTI_DEFINITIONS(Camera)

Camera::Camera(void)
	:Game::NDynamicCamera(),
	 mCameraBuffer(NULL)
{
	SetPosition(NVector3(0.0f, 0.0f, 0.0f));
	SetLookAt(NVector3(0.0f, 0.0f, 1.0f));
	SetOrientation(NVector3(0.0f, 1.0f, 0.0f));
	SetRight(NVector3(0.0f, 0.0f, 1.0f));
	SetDirection(NVector3(0.0f, 0.0f, 1.0f));
	SetRotation(NVector3(0.0f, 0.0f, 0.0f));

	SetFOVDistance(float(D3DXToRadian(45)));
	SetAspectRatio(1.0f);
	SetNearPlaneDistance(0.5f);
	SetFarPlanceDistance(1000.0f);

	Initialize();
}

Camera::~Camera(void)
{
	if(mCameraBuffer)
	{
		mCameraBuffer->Release();
		MY_DELETE(mCameraBuffer);
	}
}

void Camera::Initialize()
{
	Game::NDynamicCamera::Initialize();

	D3DXMATRIX viewMatrix;
	D3DXMatrixIdentity( &viewMatrix );
	mViewMatrix = Utility::DXDToNero(viewMatrix);
}

void Camera::Update(float gameTime)
{
	Game::NDynamicCamera::Update(gameTime);

	UpdateLookAt();
	UpdateViewMatrix();
}

void Camera::ApplyTransform(const D3DXMATRIX& transform)
{
	D3DXVECTOR3 direction = Utility::NeroToDXD(mDirection);
	D3DXVECTOR3 orientation = Utility::NeroToDXD(mOrientation);
	D3DXVECTOR3 right = Utility::NeroToDXD(mRight);

	D3DXVec3TransformNormal(&direction,&direction,&transform);
	D3DXVec3Normalize(&direction,&direction);

	D3DXVec3TransformNormal(&orientation,&orientation,&transform);
	D3DXVec3Normalize(&orientation,&orientation);

	D3DXVec3Cross(&right,&direction,&orientation);
	D3DXVec3Cross(&orientation,&right,&direction);

	mDirection = Utility::DXDToNero(direction);
	mOrientation = Utility::DXDToNero(orientation);
	mRight =  Utility::DXDToNero(right);
}

void Camera::Rotate(NVector2 amount)
{
	D3DXVECTOR3 right = Utility::NeroToDXD(mRight);

	D3DXMATRIX yRotationMatrix;
	D3DXMatrixRotationY(&yRotationMatrix,amount.X);

	D3DXMATRIX aRotationMatrix;
	D3DXMatrixRotationAxis(&aRotationMatrix,&right,amount.Y);

	D3DXMatrixMultiply(&yRotationMatrix,&yRotationMatrix,&aRotationMatrix);
	ApplyTransform(yRotationMatrix);
}

void Camera::UpdateLookAt()
{
	mLookAt = mPosition + mDirection;
}

void Camera::UpdateViewMatrix()
{
	Graphics::D3DDevice* nDevice = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();

	D3DXVECTOR3 position = Utility::NeroToDXD(mPosition);
	D3DXVECTOR3 orientation = Utility::NeroToDXD(mOrientation);
	D3DXVECTOR3 lookAt = Utility::NeroToDXD(mLookAt);
	D3DXMATRIX viewMatrix = Utility::NeroToDXD(mViewMatrix);

	D3DXMatrixLookAtLH( &viewMatrix, &position, &lookAt, &orientation);

#ifdef DX_9
	HRESULT result = nDevice->GetDevice()->SetTransform(D3DTS_VIEW,&world);
	CHECK(result)
#endif
	mPosition = Utility::DXDToNero(position);
	mOrientation = Utility::DXDToNero(orientation);
	mLookAt = Utility::DXDToNero(lookAt);
	mViewMatrix = Utility::DXDToNero(viewMatrix);
}

void Camera::Move(NVector3 directon)
{
	D3DXVECTOR3 directionVector = Utility::NeroToDXD(mDirection);
	D3DXVECTOR3 right = Utility::NeroToDXD(mRight);
	D3DXVECTOR3 movement = (D3DXVECTOR3(0,0,0));

	if( directon.Z > 0)
	{
		movement  += mThrust * directionVector;
	}
	else if( directon.Z < 0)
	{
		movement  -= mThrust * directionVector;
	}

	if( directon.X > 0)
	{
		movement += mStrafe * right;
	}
	else if( directon.X < 0)
	{
		movement -= mStrafe * right;
	}

	D3DXVECTOR3 position = Utility::NeroToDXD(mPosition);
	position += movement;
	mPosition = Utility::DXDToNero(position);
}

D3DXMATRIX Camera::CalcualteReflectionMatrix(float distanceFromSurface)
{
	D3DXMATRIX reflectionMatrix;

	D3DXVECTOR3 up, position, lookAt;
	float radians;

	// Setup the vector that points upwards.
	up.x = 0.0f;
	up.y = 1.0f;
	up.z = 0.0f;

	// Setup the position of the camera in the world.
	// For planar reflection invert the Y position of the camera.
	position.x = mPosition.X;
	position.y = -mPosition.Y + (distanceFromSurface * 2.0f);
	position.z = mPosition.Z;

	// Calculate the rotation in radians.
	radians = mRotation.Y* 0.0174532925f;

	// Setup where the camera is looking.
	lookAt.x = sinf(radians) + mPosition.X;
	lookAt.y = position.y;
	lookAt.z = cosf(radians) + mPosition.Z;

	// Create the view matrix from the three vectors.
	D3DXMatrixLookAtLH(&reflectionMatrix, &position, &lookAt, &up);
	return reflectionMatrix;
}